package cc.mrbird.febs.common.interceptor;

import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.SortedMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.HtmlUtils;

import com.google.common.collect.Maps;

import cc.mrbird.febs.common.annotation.IgnoreSign;
import cc.mrbird.febs.common.entity.FebsConstant;
import cc.mrbird.febs.common.exception.SignInvalidException;
import cc.mrbird.febs.common.utils.FebsUtil;
import cc.mrbird.febs.common.utils.SignUtil;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;

/**
 * 客户端签名验证拦截器
 *
 * @author Lord
 */
public class ClientSignInterceptor implements HandlerInterceptor {

    /**
     * 在请求处理之前进行调用（Controller方法调用之前）
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        // 获取接口请求的路径，如：/admin/sys/user/sysUserList
        String apiUrl = request.getServletPath();

        // 只对客户端接口进行签名验证，管理系统直接跳过
        if (BooleanUtil.isFalse(FebsUtil.matchURL("/member/**", apiUrl))) {
            return true;
        }

        // 判断是否忽略token的验证 将handler强转为HandlerMethod, 前面已经证实这个handler就是HandlerMethod
        HandlerMethod handlerMethod = (HandlerMethod) handler;

        // 从方法处理器中获取出要调用的方法
        Method method = handlerMethod.getMethod();

        // 获取出方法上的@IgnoreSign注解
        IgnoreSign ignoreSign = method.getAnnotation(IgnoreSign.class);

        // 如果注解不为null, 说明不需要拦截, 直接放过
        if (ObjectUtil.isNull(ignoreSign)) {

            final String sign = request.getHeader(FebsConstant.SIGN);
            if (StrUtil.isEmpty(sign)) {
                throw new SignInvalidException("签名验证出错");
            }

            // 用来组装计算签名时的参数
            SortedMap<String, Object> paramMap = Maps.newTreeMap();

            // 所有的请求参数
            Enumeration<String> paramNames = request.getParameterNames();
            while (paramNames.hasMoreElements()) {
                String paramName = paramNames.nextElement();
                String paramValue = request.getParameter(paramName);

                // 处理HTML编码问题，会自动将双引号等编码为&quot; 直接进行解码，如果是正常字符串不会受影响
                paramValue = HtmlUtils.htmlUnescape(paramValue);

                paramMap.put(paramName, paramValue);
            }

            // 服务器端生成签名
            String mySign = SignUtil.createSign(apiUrl, paramMap);

            // 两方签名不一致时，直接抛出去
            if (!StrUtil.equals(sign, mySign)) {
                throw new SignInvalidException("签名验证出错");
            }

            return true;
        }

        // 只有返回true才会继续向下执行，返回false取消当前请求
        return true;
    }

    /**
     * 请求处理之后进行调用，但是在视图被渲染之前（Controller方法调用之后）
     */
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o,
            ModelAndView modelAndView) throws Exception {
    }

    /**
     * 在整个请求结束之后被调用，也就是在DispatcherServlet 渲染了对应的视图之后执行（主要是用于进行资源清理工作）
     */
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
            Object o, Exception e) throws Exception {
    }

}
